/**
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.merak.core.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

import org.merak.core.model.SettingException;

/**
 * Copyright © 2010-2012 Merak Computing. All rights reserved.<br><br>
 *
 * Class specialized in loading settings files.
 *
 * @author fmarinho.
 * @version 1.00.00 - Aug 27, 2012 - fmarinho - initial version.
 *
 * @since MerakCore 1.00.00
 */
public class XMLLoader {

	//~ Constants ////////////////////////////////////////////////////////////////
	//****************************************************************************
	private static final String W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema";

	//~ Attributes ///////////////////////////////////////////////////////////////
	//****************************************************************************
	private InputStream schema = null;

	//~ Methods //////////////////////////////////////////////////////////////////
	//****************************************************************************
	/**
	 * Loads the schema file from the file system.
	 *
	 * @param file The input file
	 *
	 * @since v 1.00.00
	 */
	public void setSchema(File file) {

		try {
			this.schema = new FileInputStream(file);
		}
		catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}

	}

	/**
	 * Loads the schema file as resource.
	 *
	 * @param resource The input file
	 *
	 * @since v 1.00.00
	 */
	public void setSchema(String resource) {

		ClassLoader loader = this.getClass().getClassLoader();
		this.schema = loader.getResourceAsStream(resource);

	}

	/**
	 * Loads configurations from an input stream to a new Settings object.
	 * The xml is validated against org/merak/core/settings/Application.xsd.
	 *
	 * @param input The input stream to be read.
	 * @return A new Settings object reflecting the content of the read input stream.
	 * @throws SettingException If a parse or IO failure occurs.
	 *
	 * @since v 1.00.00
	 */
	public Object load(InputStream input,Class<?> type) throws SettingException {

        try {
            // Now, JABX can be prepared to read
            JAXBContext  context = JAXBContext.newInstance(type);
            Unmarshaller parser  = context.createUnmarshaller();

            // Create a Schema for validation
            if (this.schema != null) {
                SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
            	Source        source  = new StreamSource(this.schema);
                Schema        schema  = factory.newSchema(source);
                parser.setSchema(schema);
            }

        	// Do read the xml now!!
        	return parser.unmarshal(input);
        }
        catch (JAXBException e) {
        	throw new SettingException(e);
        }
        catch (SAXException e) {
        	throw new SettingException(e);
		}

	}

	/**
	 * Loads configurations from a file to a new Settings object.
	 * The xml is validated against org/merak/core/settings/Application.xsd.
	 *
	 * @param file The file stream to be load.
	 * @return new Settings object reflecting the content of the read input stream.
	 * @throws SettingException If a parse or IO failure occurs.
	 *
	 * @since v 1.00.00
	 */
	public Object load(File input,Class<?> type) throws SettingException {

		try {
			return this.load( new FileInputStream(input), type );
		}
		catch (FileNotFoundException e) {
			throw new SettingException( "Configuration file is missing: " + input );
		}

	}

}
